Add collapsible console sections with marker detection - #1267
Add collapsible console sections with marker detection#1267adityajalkhare wants to merge 36 commits into
Conversation
…ith detection logic for console output markers
…nd remove built-in rule checks
- Update default rendering behavior for small closed groups to be open. - Add logic to collapse groups with more than 25 children. - Refactor ConsoleSection component to handle open state based on child count. - Improve styling for chevron indicator in console sections. - Update tests to reflect changes in rendering logic and title trimming.
|
@timja I see that this failed due to an infrastructure issue, is there a way to retrigger it? |
I can re-trigger but for you to do it independently you can:
|
|
(conflicts) |
| enabledByDefault: boolean; | ||
| } | ||
|
|
||
| export async function getConsoleSectionRules( |
There was a problem hiding this comment.
I don't think you've committed everything, nothing seems to call this and I don't get any groups when I test this
There was a problem hiding this comment.
please check now
|
|
||
| @Test | ||
| void markerAnnotatorIsDiscovered(JenkinsRule j) { | ||
| List<ConsoleSectionAnnotator> annotators = |
There was a problem hiding this comment.
Can we get some sort of integration test to make sure these:
- show up in the UI.
- Clicking collapses them
- When clicked the text isn't present
- Clicking again restores the group
There was a problem hiding this comment.
Added test for this too
|
@timja Thanks for the feedback, I will look at these in sometime. @novinxy There is not a skip between each group, just the collapsible headers consume lines which are hidden when it renders as collapsible. Changing the line numbering to honor ignoring headers would/might break existing functionality with links to console with line numbers, and also require too much change... is what I understood. Maybe I am wrong here. Maybe this screenshot can help understand what is happening in the background - |
…ogStream with section rules
…ph-view-plugin into feature/collapsible-console-elements
….com/adityajalkhare/pipeline-graph-view-plugin into feature/collapsible-console-elements
….com/adityajalkhare/pipeline-graph-view-plugin into feature/collapsible-console-elements
|
Could you fix the lint issues please |
|
I don't think I'll have time to look over it until the weekend so I'm happy for it to be merged before then if it will be holding things up. I like the idea of what has been done. |
| * Processes console log text through registered {@link ConsoleSectionAnnotator} | ||
| * instances and collects section boundary events. | ||
| * | ||
| * <p>Annotator instances may be shared singletons (e.g. from | ||
| * {@code ExtensionList}). All access to annotator state ({@code reset()}, | ||
| * {@code detect()}) is synchronized so concurrent requests are safe. | ||
| */ |
There was a problem hiding this comment.
Fixed. The constructor now clones each annotator via ConsoleSectionAnnotator.clone() (the class now implements Cloneable), so shared singleton instances from ExtensionList are never mutated by concurrent requests. Removed the synchronized blocks since they're no longer needed - each processor owns its own annotator instances.
| public List<BoundaryEvent> process(String logText) { | ||
| if (annotators.isEmpty() || logText.isEmpty()) { | ||
| return Collections.emptyList(); | ||
| } | ||
|
|
||
| String[] lines = logText.split("\n", -1); | ||
| List<BoundaryEvent> events = new ArrayList<>(); | ||
|
|
There was a problem hiding this comment.
Fixed. process(String) now delegates to process(InputStream) via new ByteArrayInputStream(logText.getBytes(UTF_8)), so both paths use the same BufferedReader.readLine() line-counting semantics.
| // Read raw plain text (not HTML) - annotators expect undecorated output. | ||
| // We buffer into a byte array because writeLogTo requires an OutputStream, | ||
| // then stream line-by-line through the processor to avoid a second full copy. | ||
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); | ||
| logText.writeLogTo(0L, baos); | ||
|
|
||
| ConsoleSectionProcessor processor = new ConsoleSectionProcessor( | ||
| ConsoleSectionAnnotator.all().stream().toList()); | ||
| List<ConsoleSectionProcessor.BoundaryEvent> events = | ||
| processor.process(new java.io.ByteArrayInputStream(baos.toByteArray())); | ||
|
|
There was a problem hiding this comment.
Fixed. Replaced the ByteArrayOutputStream buffer + toByteArray() copy with a custom LineProcessingOutputStream that feeds lines directly into the annotators as writeLogTo pushes bytes. Each line is processed and discarded immediately - the full log is never held in memory. The per-line buffer is a small 512-byte ByteArrayOutputStream that gets reset after every newline.
| const [boundaries, setBoundaries] = useState<ConsoleSectionBoundary[]>([]); | ||
| useEffect(() => { | ||
| let cancelled = false; | ||
| getConsoleSectionBoundaries(currentRunPath, stepId) | ||
| .then((data) => { | ||
| if (!cancelled) { | ||
| setBoundaries(data); | ||
| } | ||
| return data; | ||
| }) | ||
| .catch(() => {}); | ||
| return () => { | ||
| cancelled = true; | ||
| }; | ||
| }, [currentRunPath, stepId]); | ||
|
|
||
| const sectionTree = useMemo( | ||
| () => | ||
| applyAnnotatorBoundaries( | ||
| applyRulesToSections( | ||
| parseConsoleSections(logBuffer.lines), | ||
| sectionRules, | ||
| ), | ||
| boundaries, | ||
| ), | ||
| [logBuffer.lines, sectionRules, boundaries], | ||
| ); |
There was a problem hiding this comment.
Fixed. Added logComplete (derived from logBuffer.stopTailing) to the boundaries useEffect dependency array. Boundaries now re-fetch when the log transitions from streaming to complete, picking up any sections or section-ends that arrived after the initial fetch.
|
@timja weird... I tested it just now with the file you gave and it seems to work for me - I will check the copilot review comments. |
- Introduced logComplete state in ConsoleLogStream to manage log tailing. - Updated dependencies in ConsoleSection to use a div for section titles. - Modified console-section.scss to set title display to inline. - Made ConsoleSectionAnnotator cloneable for per-request isolation. - Streamlined log processing in ConsoleSectionProcessor to handle input streams directly. - Improved PipelineConsoleViewAction to process logs without full buffering.
|
I was able to reproduce the issue. You have the Timestamper plugin installed. After Will work on fixing it... |
|
@timja you should be able to see console sections collapsing now with Timestamper installed |
Thanks I'll look when I get a chance. |








Fixes #1186
Adds collapsible sections to the console output view. Steps with
##[group]Title/##[endgroup]or::group::Title/::endgroup::markers in their console output now render as collapsible sections within the step's log.Features
##[group]/::group::) in console output and renders them as collapsible<details>sections with a title and line count badge.+(fromset -x) are not treated as markers.##[group]can be closed with::endgroup::and vice versa.@ExtensiononConsoleSectionRule. No built-in rules are shipped.Design
--standard-transitionfor the rotate animation.Files
parseConsoleSections.ts: parser that converts flat console lines into a tree of groups and linesConsoleSection.tsx: React component rendering collapsible<details>elementsconsole-section.scss: styling with SVG chevron and line count badgeConsoleSectionRule.java:ExtensionPointfor plugin-contributed rulesConsoleSectionAnnotator.java/MarkerConsoleSectionAnnotator.java: server-side annotatorPipelineConsoleViewAction.java: API endpoint for section rulesRestClient.tsx: client-side fetch for section rulesTesting
docs/examples/collapsible-sections-test.jenkinsfilecovering all edge casesScreenshots
Expanded
Collapsed
Nested Collapsible Sections
Colored
Submitter checklist